using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TerrainScript : MonoBehaviour {
	
	public TerrainData terrainData;
	
	//noise variables
	const int WIDTH = 16;
    const int HEIGHT = 16;
    float[,] textureData = new float[WIDTH, HEIGHT];
	float[, ,] splatMapData;
	
	Rect levelTerrain;
	float levelTerrainBorder;
	public List<TreeInstance> trees = new List<TreeInstance>();
	
	public float Remap(float Y, float A, float B, float C, float D)
	{
		return (Y-A)/(B-A) * (D-C) + C;
	}
	
	public float UnitMap(float t, float minVal = 0.0f, float maxVal = 1.0f)
	{
		float result = (t - minVal) * (1.0f / (maxVal - minVal));
		return Mathf.Clamp(result, 0.0f, 1.0f);
	}
	
	// Use this for initialization
	void Start () {
		Random.seed = 1;
		levelTerrain = new Rect(50, 50, 25, 25);
		levelTerrainBorder = (float)Mathf.Round(levelTerrain.width * 0.5f);
		
		float sample = 0.0f;
		float height = 0.0f;
		float[,] heights = new float[256, 256];
		splatMapData = new float[terrainData.alphamapWidth, terrainData.alphamapHeight, terrainData.alphamapLayers];
		
		//Debug.Log(x + " " + y);
		
		GenerateNoiseTexture();
		for(int x = 0; x < 256; x++)
		{
			for(int y = 0; y < 256; y++)
			{	
				//float height = (UnityEngine.Mathf.Sin(x / 25.0f) + 
				//heights[x,y] = UnityEngine.Mathf.Clamp(x+y, 0.0f, 1.0f); 
				
				//within the leveled terrain
				if(x >= levelTerrain.x && x <= levelTerrain.x + levelTerrain.width &&
					y >= levelTerrain.y && y <= levelTerrain.y + levelTerrain.height)
				{
					//set the flat terrain piece to a specfic height with a different texture
					height = 0.25f;
					heights[x,y] = height;
					
					//set texture to dirt
					splatMapData[x, y, 3] = 1.0f;
				}else{
					sample = SampleTexture((float)(x / 256.0f), (float)(y / 256.0f));
					
					height = sample * 0.35f;
					//heights[x,y] = Random.value; //random heights ie:just noise
					heights[x,y] = height;
					
					float mapped = Remap(height, 0.0f, 0.35f, 0.0f, 1.0f);
					//there is grass in this location
					if(mapped < 0.6f)
					{
						
						if(Mathf.Round(Random.value * 2) == 0)
						{
							Vector3 treePosition;
							treePosition.x = (float)(x / 256.0f);		//x position on the noise map
							treePosition.y = terrainData.GetInterpolatedHeight(x, y) / terrainData.size.y;
							treePosition.z = (float)(y / 256.0f);		//y position on the noise map
							
							TreeInstance newTree = new TreeInstance();
							newTree.position = treePosition;
							newTree.prototypeIndex = 0;
							newTree.widthScale = 1;
							newTree.heightScale = 1;
							newTree.color = Color.white;
							
							//trees.Add(newTree);
							
							//Instantiate(Resources.Load("Palm"), new Vector3(x + 0.5f, terrainData.GetInterpolatedHeight(x, y) / terrainData.size.y, y + 0.5f), Quaternion.identity);
						}
					}
					
					//assign a texture to this position
					splatMapData[x, y, 0] = sample < 0.6f ? 1.0f : 0.0f;
					if(sample >= 0.6f && sample <= 0.7f) { splatMapData[x, y, 1] = 1.0f; }
						else { splatMapData[x, y, 1] = 0.0f; }
					splatMapData[x, y, 2] = sample < 0.70f ? 0.0f : 1.0f;
					
					
				}
			}
		}
		
		terrainData.treeInstances = trees.ToArray();
		
		//terrainData.heightmapWidth = 256;
		//terrainData.heightmapHeight = 256;
		terrainData.SetHeights(0, 0, heights);
		
		terrainData.SetAlphamaps(0, 0, splatMapData);
		
		//outputs all the detail meshes' textures this terrain contains
		foreach(DetailPrototype detail in terrainData.detailPrototypes)
		{
			Debug.Log(detail.prototypeTexture);
		}
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	public void GenerateNoiseTexture()
    {
        //fill this texture with white noise
        for (int x = 0; x < WIDTH; x++)
        {
            for (int y = 0; y < HEIGHT; y++)
            {
                //values range from 0 - 1
                textureData[x, y] = (Random.value) * 1.0f;
                //Console.WriteLine("TextureData: " + x + "|" + y + " = " +  textureData[x, y]);
            }
        }
    }

    public float SampleTexture(float u, float v)
    {
		float result = 0.0f;
		float totalWeight = 0.0f;
		for(int i = 1; i < 5; i++)
		{
			float power = Mathf.Pow(2, i);
			float weight = 1.0f / power;
			result += Mathf.Abs(SampleRaw(u * power, v * power)) * weight;
			totalWeight += weight;
			
		}
		return result / totalWeight;
		
        float layer1 = Mathf.Abs(SampleRaw(u, v));
        float layer2 = Mathf.Abs(SampleRaw(u * 2, v * 2)) * 0.5f;
        float layer3 = Mathf.Abs(SampleRaw(u * 4, v * 4)) * 0.25f;
        float layer4 = Mathf.Abs(SampleRaw(u * 8, v * 8)) * 0.125f;
        float layer5 = Mathf.Abs(SampleRaw(u * 12, v * 12)) * 0.0625f;
        //return layer1;
        //Console.WriteLine(Math.Sin((double)( layer1 + layer2 + layer3 + layer4 + layer5)));
        //return (layer1 + layer2 + layer3 + layer4 + layer5) *0.20f;
        return (float)(layer1 + layer2 + layer3 + layer4 + layer5);//*0.20f;
    }

    private float SampleRaw(float u, float v)
    {
        //get the decimal value
        u = u % 1.0f;
        v = v % 1.0f;

        //get position the noise texture to sample from
        int xCell = (int)(u * WIDTH);
        int yCell = (int)(v * HEIGHT);

        int nextxCell = (xCell + 1) % WIDTH;
        int nextyCell = (yCell + 1) % HEIGHT;

        //retrieve intensity data of of surroundering pixel corners
        float upperLeftIntensity = textureData[xCell, yCell];
        float upperRightIntensity = textureData[nextxCell, yCell];
        float lowerLeftIntensity = textureData[xCell, nextyCell];
        float lowerRightIntensity = textureData[nextxCell, nextyCell];

        //portion of the edges surrounding this pixel to blend
        float tX = (u * WIDTH) % 1;
        float tY = (v * HEIGHT) % 1;

        //troll the noise with blend
        float upperBlend = Mathf.SmoothStep(upperLeftIntensity, upperRightIntensity, tX);
        float lowerBlend = Mathf.SmoothStep(lowerLeftIntensity, lowerRightIntensity, tX);

        float finalBlend = Mathf.SmoothStep(upperBlend, lowerBlend, tY);

        return finalBlend;
    }
	
}
